﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.ServiceModel;
using Informant.Contacts;

namespace Informant.Clients.DataProviders.Wpf4.InformantService
{
	internal class InformantServiceContext : IContactServiceCallback, ISmsServiceCallback
	{
		#region Fields

		private ContactServiceClient _contactServiceClient;
		private SmsServiceClient _smsServiceClient;
		private Boolean _isContactServiceLoggedOn;
		private Boolean _isSmsServiceLoggedOn;
		private String _contactServiceFailureMessage;
		private String _smsServiceFailureMessage;
		private Boolean _contactServiceResponded;
		private Boolean _smsServiceResponded;

		#endregion

		#region Construction

		/// <summary>
		/// Initializes a new instance of the <see cref="InformantServiceContext"/> class.
		/// </summary>
		public InformantServiceContext() { }

		#endregion

		#region Events

		public event EventHandler<LogOnCompletedEventArgs> LogOnAsyncCompleted;
		private void OnLogOnAsyncCompleted(LogOnCompletedEventArgs e)
		{
			var handler = LogOnAsyncCompleted;
			if (handler != null)
			{
				handler(this, e);
			}
		}
		public event EventHandler<LogOffCompletedEventArgs> LogOffAsyncCompleted;
		private void OnLogOffAsyncCompleted(LogOffCompletedEventArgs e)
		{
			var handler = LogOffAsyncCompleted;
			if (handler != null)
			{
				handler(this, e);
			}
		}
		public event EventHandler<RetrieveContactsEventArgs> RetrieveContactsAsyncCompleted;
		private void OnRetrieveContactsAsyncCompleted(RetrieveContactsEventArgs e)
		{
			var handler = RetrieveContactsAsyncCompleted;
			if (handler != null)
			{
				handler(this, e);
			}
		}
		public event EventHandler<RetrieveGroupsEventArgs> RetrieveGroupsAsyncCompleted;
		private void OnRetrieveGroupsAsyncCompleted(RetrieveGroupsEventArgs e)
		{
			var handler = RetrieveGroupsAsyncCompleted;
			if (handler != null)
			{
				handler(this, e);
			}
		}
		public event EventHandler<SendSmsProgressReportedEventArgs> SendSmsAsyncProgressReported;
		private void OnSendSmsAsyncProgressReported(SendSmsProgressReportedEventArgs e)
		{
			var handler = SendSmsAsyncProgressReported;
			if (handler != null)
			{
				handler(this, e);
			}
		}
		public event EventHandler<SendSmsCompletedEventArgs> SendSmsAsyncCompleted;
		private void OnSendSmsAsyncCompleted(SendSmsCompletedEventArgs e)
		{
			var handler = SendSmsAsyncCompleted;
			if (handler != null)
			{
				handler(this, e);
			}
		}

		#endregion

		#region Properties

		/// <summary>
		/// Gets a value indicating whether both the Contact and Sms Services are logged on.
		/// </summary>
		/// <value><c>true</c> if [logged on]; otherwise, <c>false</c>.</value>
		public Boolean LoggedOn
		{
			get
			{
				Boolean loggedOn = false;
				if (_contactServiceClient != null && _smsServiceClient != null
					&& _isContactServiceLoggedOn && _isSmsServiceLoggedOn
					&& _contactServiceClient.State == CommunicationState.Opened && _smsServiceClient.State == CommunicationState.Opened)
				{
					loggedOn = true;
				}
				return loggedOn;
			}
		}

		#endregion

		#region Public Methods

		/// <summary>
		/// Logs the user on.
		/// </summary>
		/// <param name="username">The username.</param>
		/// <param name="password">The password.</param>
		/// <param name="callback">The callback.</param>
		public void LogOnAsync(String username, String password)
		{
			InitializeContactService();
			InitializeSmsService();
			_contactServiceResponded = false;
			_smsServiceResponded = false;

			//FUTURE: Refactor these two Async calls to be sync and place in an Async call together.
			//	This will aid in bringing the two threads together.
			_contactServiceClient.BeginLogOn(username, password, EndLogOnContactServiceCallback, null);
			_smsServiceClient.BeginLogOn(username, password, EndLogOnSmsServiceCallback, null);
		}
		/// <summary>
		/// Logs the user off.
		/// </summary>
		/// <returns></returns>
		public void LogOffAsync()
		{
			_contactServiceResponded = false;
			_smsServiceResponded = false;

			if (_contactServiceClient != null && _contactServiceClient.State == CommunicationState.Opened)
			{
				_contactServiceClient.BeginLogOff(EndLogOffContactServiceCallback, null);
			}
			else
			{
				ClearContactService();
			}

			if (_smsServiceClient != null && _smsServiceClient.State == CommunicationState.Opened)
			{
				_smsServiceClient.BeginLogOff(EndLogOffSmsServiceCallback, null);
			}
			else
			{
				ClearSmsService();
			}
		}
		/// <summary>
		/// Retrieves the contacts.
		/// </summary>
		/// <returns></returns>
		public void RetrieveContactsAsync()
		{
			_contactServiceClient.RetrieveAllContactsAsync();
		}
		/// <summary>
		/// Retrieves the groups.
		/// </summary>
		/// <returns></returns>
		public void RetrieveGroupsAsync()
		{
			_contactServiceClient.RetrieveAllGroupsAsync();
		}
		/// <summary>
		/// Sends the SMS.
		/// </summary>
		/// <param name="sms">The SMS.</param>
		/// <param name="callback">The callback.</param>
		/// <param name="progressCallback">The progress callback.</param>
		/// <exception cref="Informant.ErrorHandling.SmsException"/>
		public void SendSmsAsync(Sms sms)
		{
			_smsServiceClient.SendSmsAsync(sms);
		}

		#endregion

		#region Private Methods

		/// <summary>
		/// Initializes the contact service.
		/// </summary>
		private void InitializeContactService()
		{
			_contactServiceClient = new ContactServiceClient(new InstanceContext(this));
		}
		/// <summary>
		/// Initializes the SMS service.
		/// </summary>
		private void InitializeSmsService()
		{
			_smsServiceClient = new SmsServiceClient(new InstanceContext(this));
		}
		/// <summary>
		/// Clears the contact service.
		/// </summary>
		private void ClearContactService()
		{
			_contactServiceClient = null;
		}
		/// <summary>
		/// Clears the SMS service.
		/// </summary>
		private void ClearSmsService()
		{
			_smsServiceClient = null;
		}
		/// <summary>
		/// Handles the async callback of both ContactService and SmsService.
		/// </summary>
		private void SyncronizeLogOn()
		{
			if (_contactServiceResponded && _smsServiceResponded)
			{
				OnLogOnAsyncCompleted(new LogOnCompletedEventArgs(LoggedOn, CreateFailureMessage()));
				_contactServiceResponded = false;
				_smsServiceResponded = false;
			}
		}
		/// <summary>
		/// Logs the off handler.
		/// </summary>
		private void SyncronizeLogOff()
		{
			if (_contactServiceResponded && _smsServiceResponded)
			{
				OnLogOffAsyncCompleted(new LogOffCompletedEventArgs(LoggedOn));
				_contactServiceResponded = false;
				_smsServiceResponded = false;
			}
		}
		/// <summary>
		/// Creates the failure message.
		/// </summary>
		/// <returns></returns>
		private String CreateFailureMessage()
		{
			//FUTURE: For AccountLocked errors provide web access to link.
			String failureMessage = null;
			if (!LoggedOn)
			{
				if (_contactServiceFailureMessage == _smsServiceFailureMessage)
				{
					failureMessage = _contactServiceFailureMessage;
				}
				else
				{
					failureMessage = String.Format(
						String.Concat("Error 1: {0}", Environment.NewLine, "Error 2: {1}"),
						_contactServiceFailureMessage,
						_smsServiceFailureMessage);
				}
			}
			return failureMessage;
		}

		#endregion

		#region Callback Methods

		/// <summary>
		/// Callback for LogOnContactService.
		/// </summary>
		/// <param name="loggedOn">if set to <c>true</c> [logged on].</param>
		public void LogOnContactServiceCallback(Boolean loggedOn)
		{
			_contactServiceResponded = true;
			_isContactServiceLoggedOn = loggedOn;
			SyncronizeLogOn();
		}
		/// <summary>
		/// Ends the log on contact service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOnContactServiceCallback(IAsyncResult result)
		{
			try
			{
				_contactServiceClient.EndLogOn(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				_contactServiceFailureMessage = ex.Detail.Message;
				LogOnContactServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOnContactServiceCallback(false);
			}
		}
		/// <summary>
		/// Callback for LogOffContactService.
		/// </summary>
		/// <param name="loggedOff">if set to <c>true</c> [logged off].</param>
		public void LogOffContactServiceCallback(Boolean loggedOff)
		{
			_contactServiceResponded = true;
			SyncronizeLogOff();
		}
		/// <summary>
		/// Ends the log off contact service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOffContactServiceCallback(IAsyncResult result)
		{
			try
			{
				_contactServiceClient.EndLogOff(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				LogOffContactServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOffContactServiceCallback(false);
			}
			ClearContactService();
		}
		/// <summary>
		/// Callback for RetrieveAllContacts.
		/// </summary>
		/// <param name="contacts">The contacts.</param>
		public void RetrieveAllContactsCallback(List<Contact> contacts)
		{
			OnRetrieveContactsAsyncCompleted(new RetrieveContactsEventArgs(contacts.OfType<IContact>()));
		}
		/// <summary>
		/// Ends the retrieve all contacts callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndRetrieveAllContactsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for RetrieveAllGroups.
		/// </summary>
		/// <param name="groups">The groups.</param>
		public void RetrieveAllGroupsCallback(List<Group> groups)
		{
			OnRetrieveGroupsAsyncCompleted(new RetrieveGroupsEventArgs(groups.OfType<IGroup>()));
		}
		/// <summary>
		/// Ends the retrieve all groups callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndRetrieveAllGroupsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}

		/// <summary>
		/// Callback for LogOnSmsService.
		/// </summary>
		/// <param name="loggedOn">if set to <c>true</c> [logged on].</param>
		public void LogOnSmsServiceCallback(Boolean loggedOn)
		{
			_smsServiceResponded = true;
			_isSmsServiceLoggedOn = loggedOn;
			SyncronizeLogOn();
		}
		/// <summary>
		/// Ends the log on SMS service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOnSmsServiceCallback(IAsyncResult result)
		{
			try
			{
				_smsServiceClient.EndLogOn(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				_smsServiceFailureMessage = ex.Detail.Message;
				LogOnSmsServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOnSmsServiceCallback(false);
			}
		}
		/// <summary>
		/// Callback for LogOffSmsService.
		/// </summary>
		/// <param name="loggedOff">if set to <c>true</c> [logged off].</param>
		public void LogOffSmsServiceCallback(Boolean loggedOff)
		{
			_smsServiceResponded = true;
			SyncronizeLogOff();
		}
		/// <summary>
		/// Ends the log off SMS service callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndLogOffSmsServiceCallback(IAsyncResult result)
		{
			try
			{
				_smsServiceClient.EndLogOff(result);
			}
			catch (FaultException<AuthorizationFault> ex)
			{
				LogOffSmsServiceCallback(false);
			}
			catch (FaultException ex)
			{
				LogOffSmsServiceCallback(false);
			}
			ClearSmsService();
		}
		/// <summary>
		/// Callback for DeleteSms.
		/// </summary>
		/// <param name="smsId">The SMS id.</param>
		/// <param name="deleted">if set to <c>true</c> [deleted].</param>
		public void DeleteSmsCallback(String smsId, Boolean deleted)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Ends the delete SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndDeleteSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for MarkReadSms.
		/// </summary>
		/// <param name="smsId">The SMS id.</param>
		/// <param name="isRead">if set to <c>true</c> [is read].</param>
		public void MarkReadSmsCallback(String smsId, Boolean isRead)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Ends the mark read SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndMarkReadSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for RetrieveAllSms.
		/// </summary>
		/// <param name="allSms">All SMS.</param>
		public void RetrieveAllSmsCallback(List<Sms> allSms)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Ends the retrieve all SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndRetrieveAllSmsCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}
		/// <summary>
		/// Callback for SendSms.
		/// </summary>
		/// <param name="successful">if set to <c>true</c> [successful].</param>
		public void SendSmsCallback(Boolean successful)
		{
			OnSendSmsAsyncCompleted(new SendSmsCompletedEventArgs(successful));
		}
		/// <summary>
		/// Ends the send SMS callback.
		/// </summary>
		/// <param name="result">The result.</param>
		public void EndSendSmsCallback(IAsyncResult result)
		{
			try
			{
				_smsServiceClient.EndLogOff(result);
			}
			catch (FaultException<SendSmsFault> ex)
			{
				OnSendSmsAsyncCompleted(new SendSmsCompletedEventArgs(false, ex.Detail.RejectedRecipients.OfType<IPhone>()));
			}
		}
		/// <summary>
		/// Callback for SendSmsProgress.
		/// </summary>
		/// <param name="sent">The sent.</param>
		/// <param name="failed">The failed.</param>
		/// <param name="total">The total.</param>
		public void SendSmsProgressCallback(Int32 sent, Int32 failed, Int32 total)
		{
			OnSendSmsAsyncProgressReported(new SendSmsProgressReportedEventArgs(sent, failed, total));
		}

		#region Not In Use

		public IAsyncResult BeginLogOnContactServiceCallback(Boolean loggedOn, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginLogOffContactServiceCallback(Boolean loggedOff, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginRetrieveAllContactsCallback(List<Contact> contacts, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginRetrieveAllGroupsCallback(List<Group> groups, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginLogOnSmsServiceCallback(Boolean loggedOn, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginLogOffSmsServiceCallback(Boolean loggedOff, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginDeleteSmsCallback(String smsId, Boolean deleted, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginMarkReadSmsCallback(String smsId, Boolean isRead, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginRetrieveAllSmsCallback(List<Sms> allSms, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginSendSmsCallback(Boolean successful, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public IAsyncResult BeginSendSmsProgressCallback(Int32 sent, Int32 failed, Int32 total, AsyncCallback callback, Object asyncState)
		{
			throw new NotImplementedException();
		}
		public void EndSendSmsProgressCallback(IAsyncResult result)
		{
			throw new NotImplementedException();
		}

		#endregion

		#endregion
	}
}
